package com.demo.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.constant.CacheConstants;
import com.demo.domain.SysUser;
import com.demo.domain.Vo.UserVo;
import com.demo.mapper.SysUserMapper;
import com.demo.service.SysDictService;
import com.demo.service.SysUserService;
import com.demo.utils.BeanUtils;
import com.demo.utils.PinyinUtils;
import com.github.javafaker.Faker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author gxd
 * @since 2025/5/27
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private SysDictService dictService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TransactionTemplate transactionTemplate;


    @Override
    public List<SysUser> pageList(SysUser user) {
        Map<String, String> sexDictMap = dictService.getDictMapByCode(CacheConstants.USER_SEX_CODE);
        Map<String, String> delFlagDictMap = dictService.getDictMapByCode(CacheConstants.DELETE_FLAG_CODE);

        return userMapper.pageList(user).stream()
                .map(sysUser -> {
                    sysUser.setSexMc(sexDictMap.get(sysUser.getSex()));
                    sysUser.setDelFlagMc(delFlagDictMap.get(sysUser.getDelFlag()));
                    // 脱敏 手机号、姓名
                    sysUser.setPhoneNumber(DesensitizedUtil.mobilePhone(sysUser.getPhoneNumber()));
                    sysUser.setNickName(DesensitizedUtil.chineseName(sysUser.getNickName()));
                    // 把图片转Base64字符串的代码
                    sysUser.setAvatar(Base64.encode(sysUser.getAvatar()));
                    return sysUser;
                }).collect(Collectors.toList());
    }

    @Override
    public List<SysUser> selectUserList(SysUser user) {
        return userMapper.selectUserList(user).stream()
                .map(sysUser -> {
                    String sexName = dictService.selectNameByCodeAndLabel(CacheConstants.USER_SEX_CODE, sysUser.getSex());
                    String delFlagName = dictService.selectNameByCodeAndLabel(CacheConstants.DELETE_FLAG_CODE, sysUser.getDelFlag());

                    sysUser.setSex(sexName);
                    sysUser.setDelFlag(delFlagName);
                    return sysUser;
                }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysUser addOrEdit(SysUser user) {
        Assert.notNull(user, "用户信息不能为空");
        if (StrUtil.isNotBlank(user.getUserId())) {
            SysUser userInfo = Assert.notNull(this.getById(user.getUserId()), "要修改的用户信息不存在");
            BeanUtils.copyNotEmptyProperties(user, userInfo);
            this.updateById(userInfo);
            return userInfo;
        }
        user.setDelFlag("0");
        this.save(user);
        return user;
    }

    /**
     * 关联查询-用户列表
     * @return 用户列表
     */
    @Override
    public List<SysUser> selectUserJoin() {
        return userMapper.selectUserJoin();
    }

    /**
     * 分批查询-当天每半小时一分组，求每个半小时的总数
     * @return 用户集合
     */
    @Override
    public List<UserVo> selectUserBatch() {
        List<UserVo> allList = (List<UserVo>) redisTemplate.opsForValue().get(CacheConstants.USER_COUNT_24_KEY);
        if (ObjUtil.isNull(allList)) {
            allList = buildUserBatch();
            redisTemplate.opsForValue().set(CacheConstants.USER_COUNT_24_KEY, allList);
        }
        return allList;
    }

    /**
     * 去数据库查询数据
     */
    public List<UserVo> buildUserBatch() {
        List<UserVo> allList = new ArrayList<>();
        Date date = new Date();
        String todayStr = DateUtil.format(date, "YYYY-MM-dd");
        String[][] timeRanges = {{"00:00:00", "05:59:59"},{"06:00:00", "11:59:59"},{"12:00:00", "17:59:59"},{"18:00:00", "23:59:59"}};

        List<List<String>> timeRangeList = new ArrayList<>();
        Arrays.stream(timeRanges)
                .forEach(timeArr -> {
                    List<String> timeList = new ArrayList<>();
                    timeList.add(todayStr + " " + timeArr[0]);
                    timeList.add(todayStr + " " + timeArr[1]);
                    timeRangeList.add(timeList);
                });
        timeRangeList.parallelStream().forEach(timeList -> {
            List<UserVo> userVoList = userMapper.selectUserBatch(timeList.get(0), timeList.get(1));
            allList.addAll(userVoList);
        });

        // ExecutorService fixedThreadPool = Executors.newFixedThreadPool(20);
        // List<CompletableFuture<Void>> completableFutures = new ArrayList<>();
        // timeRangeList.forEach(timeList -> {
        //     CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
        //         List<UserVo> userVoList = userMapper.selectUserBatch(timeList.get(0), timeList.get(1));
        //         allList.addAll(userVoList);
        //     }, fixedThreadPool);
        //     completableFutures.add(completableFuture);
        // });
        // CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).join();
        return allList;
    }

    /**
     * 批量更新拼音字段
     * @return 结果
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED) // 不使用事务
    @Override
    public int batchUpdatePinyin() {
        // 1. 使用游标方式分页查询，避免一次性加载全部数据
        int pageSize = 500;
        long totalCount = this.count();
        long totalPages = (totalCount + pageSize - 1) / pageSize;

        // 2. 使用信号量控制并发度
        Semaphore dbSemaphore = new Semaphore(5); // 限制同时访问数据库的线程数

        List<CompletableFuture<Void>> futures = new ArrayList<>();

        for (long pageNum = 1; pageNum <= totalPages; pageNum++) {
            final long currentPage = pageNum;

            futures.add(CompletableFuture.runAsync(() -> {
                try {
                    dbSemaphore.acquire(); // 获取数据库访问许可

                    // 3. 使用新事务处理每个批次
                    transactionTemplate.execute(status -> {
                        try {
                            // 4. 查询当前页数据
                            Page<SysUser> page = this.page(new Page<>(currentPage, pageSize, false));
                            List<SysUser> users = Optional.ofNullable(page.getRecords())
                                    .orElse(Collections.emptyList());

                            // 5. 处理拼音转换（不使用parallel避免过度并发）
                            List<SysUser> updatedUsers = users.stream()
                                    .peek(user -> {
                                        user.setDeptId(user.getDeptId().replaceAll("DEPT", "D00"));
                                        // 生成随机的中文名
                                        Faker fakerCN = new Faker(new Locale("zh-CN"));
                                        String nickName = fakerCN.name().fullName();
                                        user.setNickName(nickName);
                                        user.setPinyin(PinyinUtils.getFullPinyin(nickName));
                                        user.setPinyinSort(PinyinUtils.getFirstLetters(nickName));
                                    })
                                    .collect(Collectors.toList());

                            // 6. 分批提交
                            if (!updatedUsers.isEmpty()) {
                                this.updateBatchById(updatedUsers, 200); // 较小批次提交
                            }
                            return null;
                        } catch (Exception e) {
                            status.setRollbackOnly();
                            throw e;
                        }
                    });
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("处理被中断", e);
                } finally {
                    dbSemaphore.release(); // 释放许可
                    // 7. 添加延迟减轻数据库压力
                    try { Thread.sleep(100); } catch (InterruptedException ignored) {}
                }
            }, batchUpdateExecutor));
        }

        // 8. 等待所有任务完成
        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(2, TimeUnit.HOURS);
        } catch (TimeoutException e) {
            throw new RuntimeException("处理超时", e);
        } catch (Exception e) {
            throw new RuntimeException("批量更新失败", e);
        }
        return 1;
    }

    private static final ExecutorService batchUpdateExecutor = Executors.newFixedThreadPool(
            Math.min(8, Runtime.getRuntime().availableProcessors() * 2) // 限制最大线程数
    );

    /**
     * 拼音查询-用户名列表
     * @param user 查询条件
     * @return 结果
     */
    @Override
    public List<String> selectUserNameByPinyin(SysUser user) {
        return userMapper.selectUserNameByPinyin(user);
    }
}
